package org.chnxi.study.concurrent.atomicreference.demo1;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceDemo {

    static Person p1 = new Person(1 , "zhangsan" , 18);
    static Person p2 = new Person(1 , "zhangsan" , 18);
    static AtomicReference<Person> atomicReference = new AtomicReference<>(p2);

    public static void main(String[] args) throws Exception {
        System.out.println("原始Person="+p1);

        /**
         * T1 和 T2 并发的情况下会出现以下错误情况：
         * [T-2]person=Person{id=1, name='zhangsan', age=18}
         * [T-1]person=Person{id=1, name='zhangsan', age=18}
         * T-2 : p = Person{id=1, name='T-2|zhangsan', age=20}
         * T-1 : p = Person{id=1, name='T-1|T-2|zhangsan', age=21}
         * main : person finally is =Person{id=1, name='T-1|T-2|zhangsan', age=21}
         * T2线程虽然拿到的是18，但赋值以后，出现21的结果
         */
        System.out.println("未使用AtomicReference的情况下，对对象多个属性更新未能达到原子性操作");
        Thread t1 = new Thread(()->{
            Person cur = p1;
            System.out.println("["+Thread.currentThread().getName()+"]person="+cur);
            cur.setAge(cur.getAge() + 1);
            cur.setName(Thread.currentThread().getName()+"|"+cur.getName());
            System.out.println(Thread.currentThread().getName()+" : p = "+ cur);
        }, "T-1");

        Thread t2 = new Thread(() -> {
            Person cur = p1;
            Thread.yield();
            System.out.println("["+Thread.currentThread().getName()+"]person="+cur);
            cur.setAge(cur.getAge() + 2);
            cur.setName(Thread.currentThread().getName()+"|"+cur.getName());
            System.out.println(Thread.currentThread().getName() + " : p = " + cur);
        }, "T-2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(Thread.currentThread().getName()+" : person finally is ="+p1);
        System.out.println("=====================");

        /**
         * T3 和 T4 在使用AtomicReference的情况下，虽然都拿到了18，但计算和最终结果都正常
         * [T-3]person=Person{id=1, name='zhangsan', age=18}
         * [T-4]person=Person{id=1, name='zhangsan', age=18}
         * T-3 : p = Person{id=2, name='lisi', age=19}
         * T-4 : p = Person{id=3, name='wangwu', age=20}
         * main : person finally is =Person{id=3, name='wangwu', age=20}
         */
        Thread t3 = new Thread(()->{
            Person cur = atomicReference.get();
            System.out.println("["+Thread.currentThread().getName()+"]person="+cur);
            atomicReference.getAndSet(new Person(2 , "lisi" , cur.getAge() +1));
            System.out.println(Thread.currentThread().getName()+" : p = "+ atomicReference.get());
        } , "T-3");

        Thread t4 = new Thread(()->{
            Person cur = atomicReference.get();
            Thread.yield();
            System.out.println("["+Thread.currentThread().getName()+"]person="+cur);
            Person p = atomicReference.getAndSet(new Person(3 , "wangwu" , cur.getAge()+2));
            System.out.println(Thread.currentThread().getName()+" : p = "+ atomicReference.get());
        }, "T-4");
        t3.start();
        t4.start();
        t3.join();
        t4.join();

        System.out.println(Thread.currentThread().getName()+" : person finally is ="+atomicReference.get());
    }

    public static class Person{

        private int id;

        private String name;

        private int age;

        public Person(int id, String name , int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

}
